/* machine_uart.c */

#include <stdio.h>

#include "machine_uart.h"

#include "board_init.h"

/*
 * Declerations.
 */

/* Local functions. */
STATIC mp_obj_t machine_uart_obj_init_helper(const machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);

/* Class method, which would be called by class name. */
STATIC     void machine_uart_obj_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind);
       mp_obj_t machine_uart_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args);


/* Instance methods, which would be called by class instance name. */
STATIC mp_obj_t machine_uart_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args);
STATIC mp_obj_t machine_uart_any(mp_obj_t self_in);

/* hal related functions. */
bool     machine_uart_tx_data(uint32_t uart_id, uint8_t tx_data);
uint8_t  machine_uart_rx_data(uint32_t uart_id);
uint32_t machine_uart_rx_any(uint32_t uart_id);
void     machine_uart_irq_handler(uint32_t uart_id);



const machine_uart_obj_t *uart_find(mp_obj_t user_obj);


/*
 * Functions.
 */



STATIC mp_obj_t machine_uart_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args)
{
    /* args[0] is machine_pin_obj_t. */
    //printf("machine_uart_init()");
    return machine_uart_obj_init_helper(args[0], n_args - 1, args + 1, kw_args);
}
MP_DEFINE_CONST_FUN_OBJ_KW(machine_uart_init_obj, 1, machine_uart_init);

STATIC mp_obj_t machine_uart_any(mp_obj_t self_in)
{
    machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
    return MP_OBJ_NEW_SMALL_INT(machine_uart_rx_any(self->uart_id));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_any_obj, machine_uart_any);

void machine_uart_enable_clock(uint32_t uart_id, bool enable)
{
    switch (uart_id)
    {
        case 0u:
            RCC_EnableAPB2Periphs(RCC_APB2_PERIPH_UART1, enable);
            break;
        case 1u:
            RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_UART2, enable);
            break;
        case 2u:
            RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_UART3, enable);
            break;
        case 3u:
            RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_UART4, enable);
            break;
        case 4u:
            RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_UART5, enable);
            break;
        case 5u:
            RCC_EnableAPB2Periphs(RCC_APB2_PERIPH_UART6, enable);
            break;
        case 6u:
            RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_UART7, enable);
            break;
        case 7u:
            RCC_EnableAPB1Periphs(RCC_APB1_PERIPH_UART8, enable);
            break;
        default:
            break;
    }
}

/* parameter list. */
typedef enum
{
    UART_INIT_ARG_BAUDRATE = 0,
    UART_INIT_ARG_BITS     = 1,
    UART_INIT_ARG_PARITY   = 2,
    UART_INIT_ARG_STOP     = 3,
} machine_uart_init_arg_t;

//STATIC uint8_t machine_uart_tx_buff[MACHINE_UART_NUM][MACHINE_UART_XFER_BUFF_LEN];
uint8_t machine_uart_rx_buff[MACHINE_UART_NUM][MACHINE_UART_XFER_BUFF_LEN];

//STATIC rbuf_t machine_uart_tx_rbuf[MACHINE_UART_NUM];
rbuf_t machine_uart_rx_rbuf[MACHINE_UART_NUM];

STATIC mp_obj_t machine_uart_obj_init_helper (
    const machine_uart_obj_t *self, /* machine_uart_obj_t类型的变量，包含硬件信息 */
    size_t n_args, /* 位置参数数量 */
    const mp_obj_t *pos_args, /* 位置参数清单 */
    mp_map_t *kw_args ) /* 关键字参数清单结构体 */
{
    //printf("%machine_adc_obj_init_helper().\r\n");
    static const mp_arg_t allowed_args[] =
    {
        //[ADC_INIT_ARG_MODE] { MP_QSTR_init , MP_ARG_REQUIRED | MP_ARG_BOOL, {.u_bool = false} },
        [UART_INIT_ARG_BAUDRATE] { MP_QSTR_baudrate , MP_ARG_INT, {.u_int = 9600} },
        [UART_INIT_ARG_BITS    ] { MP_QSTR_bits     , MP_ARG_INT, {.u_int = 8}    },
        [UART_INIT_ARG_PARITY  ] { MP_QSTR_parity   , MP_ARG_INT, {.u_int = 0} },
        [UART_INIT_ARG_STOP    ] { MP_QSTR_stop     , MP_ARG_INT, {.u_int = 1}    },

    };

    /* 解析参数 */
    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
    mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);

#if 0
    /* setup pinmux. */
    GPIO_Init_Type gpio_init;
    gpio_init.Speed = GPIO_Speed_50MHz;
    /* rx pin. */
    gpio_init.Pins = ( 1u << (self->rx_pin_obj->gpio_pin) );
    gpio_init.PinMode = GPIO_PinMode_In_Floating;
    GPIO_Init(self->rx_pin_obj->gpio_port, &gpio_init);
    GPIO_PinAFConf(self->rx_pin_obj->gpio_port, gpio_init.Pins, self->rx_pin_af);
    /* tx pin. */
    gpio_init.Pins = ( 1u << (self->tx_pin_obj->gpio_pin) );
    gpio_init.PinMode = GPIO_PinMode_AF_PushPull;
    GPIO_Init(self->tx_pin_obj->gpio_port, &gpio_init);
    GPIO_PinAFConf(self->tx_pin_obj->gpio_port, gpio_init.Pins, self->tx_pin_af);
#endif
    /* setup uart. */
    UART_Init_Type uart_init;
    uart_init.ClockFreqHz   = BOARD_DEBUG_UART_FREQ; /* 48mhz, APB2. */

    if (args[UART_INIT_ARG_BAUDRATE].u_int > 0)
    {
        uart_init.BaudRate      = args[UART_INIT_ARG_BAUDRATE].u_int;
    }
    else
    {
        mp_raise_ValueError(MP_ERROR_TEXT("unavailable param: baudrate."));
    }

    if (args[UART_INIT_ARG_BITS].u_int == 8)
    {
        uart_init.WordLength    = UART_WordLength_8b;
    }
    else
    {
        mp_raise_ValueError(MP_ERROR_TEXT("unavailable param: bits."));
    }

    if (args[UART_INIT_ARG_PARITY].u_int <= 2) /* none, even, odd. */
    {
        uart_init.Parity        = args[UART_INIT_ARG_PARITY].u_int;
    }
    else
    {
        mp_raise_ValueError(MP_ERROR_TEXT("unavailable param: parity."));
    }

    if (args[UART_INIT_ARG_STOP].u_int == 1)
    {
        uart_init.StopBits      = UART_StopBits_1;
    }
    else if (args[UART_INIT_ARG_STOP].u_int == 2)
    {
        uart_init.StopBits      = UART_StopBits_2;
    }
    else
    {
        mp_raise_ValueError(MP_ERROR_TEXT("unavailable param: stop."));
    }


    /* keep the conf settings in uart_obj. */
    self->conf->baudrate = args[UART_INIT_ARG_BAUDRATE].u_int;
    //self->conf->bits     = args[UART_INIT_ARG_BITS].u_int;
    //self->conf->parity   = args[UART_INIT_ARG_PARITY].u_int;
    //self->conf->stop     = args[UART_INIT_ARG_STOP].u_int;

    uart_init.XferMode      = UART_XferMode_RxTx;
    uart_init.HwFlowControl = UART_HwFlowControl_None;


#if 0
    rbuf_init(&machine_uart_rx_rbuf[self->uart_id], machine_uart_rx_buff[self->uart_id], MACHINE_UART_XFER_BUFF_LEN);
    self->conf->rx_rbuf = &machine_uart_rx_rbuf[self->uart_id];

    machine_uart_enable_clock(self->uart_id, true);
    UART_Init(self->uart_port, &uart_init);

    /* Enable only  RX interrupt. */
    UART_EnableInterrupts(self->uart_port, UART_INT_RX_DONE, true);
    NVIC_EnableIRQ(self->uart_irqn);

    /* Enable UART. */
    UART_Enable(self->uart_port, true);
#endif

    machine_uart_hw_init(self, &uart_init);

    return mp_const_none;
}


void machine_uart_hw_init(const machine_uart_obj_t *self, UART_Init_Type * init)
{
    /* setup pinmux. */
    GPIO_Init_Type gpio_init;
    gpio_init.Speed = GPIO_Speed_50MHz;
    /* rx pin. */
    gpio_init.Pins = ( 1u << (self->rx_pin_obj->gpio_pin) );
    gpio_init.PinMode = GPIO_PinMode_In_Floating;
    GPIO_Init(self->rx_pin_obj->gpio_port, &gpio_init);
    GPIO_PinAFConf(self->rx_pin_obj->gpio_port, gpio_init.Pins, self->rx_pin_af);
    /* tx pin. */
    gpio_init.Pins = ( 1u << (self->tx_pin_obj->gpio_pin) );
    gpio_init.PinMode = GPIO_PinMode_AF_PushPull;
    GPIO_Init(self->tx_pin_obj->gpio_port, &gpio_init);
    GPIO_PinAFConf(self->tx_pin_obj->gpio_port, gpio_init.Pins, self->tx_pin_af);

    /* setup uart. */
    rbuf_init(&machine_uart_rx_rbuf[self->uart_id], machine_uart_rx_buff[self->uart_id], MACHINE_UART_XFER_BUFF_LEN);
    self->conf->rx_rbuf = &machine_uart_rx_rbuf[self->uart_id];

    //uart_init.XferMode      = UART_XferMode_RxTx;
    //uart_init.HwFlowControl = UART_HwFlowControl_None;

    machine_uart_enable_clock(self->uart_id, true);
    UART_Init(self->uart_port, init);

    /* Enable only  RX interrupt. */
    UART_EnableInterrupts(self->uart_port, UART_INT_RX_DONE, true);
    NVIC_EnableIRQ(self->uart_irqn);

    /* Enable UART. */
    UART_Enable(self->uart_port, true);
}


bool machine_uart_tx_data(uint32_t uart_id, uint8_t tx_data)
{
    const machine_uart_obj_t * uart_obj = machine_uart_objs[uart_id];
    UART_Type * uartx = uart_obj->uart_port;
    while ( 0u == (UART_STATUS_TX_EMPTY & UART_GetStatus(uartx)) )
    {}
    UART_PutData(uartx, tx_data);

    return true;
}

uint8_t machine_uart_rx_data(uint32_t uart_id)
{
    const machine_uart_obj_t * uart_obj = machine_uart_objs[uart_id];
    rbuf_t *rx_rbuf = uart_obj->conf->rx_rbuf;

    while ( rbuf_is_empty(rx_rbuf) )
    {
    }

    return rbuf_output(rx_rbuf);
}

uint32_t machine_uart_rx_any(uint32_t uart_id)
{
    const machine_uart_obj_t * uart_obj = machine_uart_objs[uart_id];
    rbuf_t *rx_rbuf = uart_obj->conf->rx_rbuf;
    return rbuf_count(rx_rbuf);
}

/* this function would be called in hardware irq. */
void machine_uart_irq_handler(uint32_t uart_id)
{
    const machine_uart_obj_t * uart_obj = machine_uart_objs[uart_id];
    UART_Type * uartx = uart_obj->uart_port;
    rbuf_t * rx_rbuf = uart_obj->conf->rx_rbuf;

    /* rx process. */
    if (   (0u != (UART_INT_RX_DONE & UART_GetEnabledInterrupts(uartx)))
        && (0u != (UART_INT_RX_DONE & UART_GetInterruptStatus(uartx))) )
    {
        if ( !rbuf_is_full(rx_rbuf) )
        {
            rbuf_input(rx_rbuf, UART_GetData(uartx)); /* read data to clear rx interrupt bits. */
        }
    }
}

void UART1_IRQHandler(void) { machine_uart_irq_handler(0u); }
void UART2_IRQHandler(void) { machine_uart_irq_handler(1u); }
void UART3_IRQHandler(void) { machine_uart_irq_handler(2u); }
void UART4_IRQHandler(void) { machine_uart_irq_handler(3u); }
void UART5_IRQHandler(void) { machine_uart_irq_handler(4u); }
void UART6_IRQHandler(void) { machine_uart_irq_handler(5u); }
void UART7_IRQHandler(void) { machine_uart_irq_handler(6u); }
void UART8_IRQHandler(void) { machine_uart_irq_handler(7u); }


STATIC void machine_uart_obj_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind)
{
    /* o is the machine_pin_obj_t. */
    (void)kind;
    const machine_uart_obj_t *self = MP_OBJ_TO_PTR(o);
    //mp_printf(print, "ADC(%s)", qstr_str(self->adc_channel));
    mp_printf(print, "UART(%d): baudrate=%d on RX(%s), TX(%s)",
        self->uart_id, self->conf->baudrate,
        qstr_str(self->rx_pin_obj->name),
        qstr_str(self->tx_pin_obj->name)
        );
}

/* return an instance of machine_pin_obj_t. */
mp_obj_t machine_uart_obj_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args)
{
    mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);

    const machine_uart_obj_t *uart_obj = uart_find(args[0]);

    if ( (n_args >= 1) || (n_kw >= 0) )
    {
        mp_map_t kw_args;
        mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); /* 将关键字参数从总的参数列表中提取出来，单独封装成kw_args。 */
        machine_uart_obj_init_helper(uart_obj, n_args - 1, args + 1, &kw_args);
    }

    return (mp_obj_t)uart_obj;
}

/* class locals_dict_table. */
STATIC const mp_rom_map_elem_t machine_uart_locals_dict_table[] =
{
    /* Class instance methods. */
    { MP_ROM_QSTR(MP_QSTR_init),     MP_ROM_PTR(&machine_uart_init_obj)             },
    { MP_ROM_QSTR(MP_QSTR_any),      MP_ROM_PTR(&machine_uart_any_obj)              },
    { MP_ROM_QSTR(MP_QSTR_read),     MP_ROM_PTR(&mp_stream_read_obj)                },
    { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
    { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj)            },
    { MP_ROM_QSTR(MP_QSTR_write),    MP_ROM_PTR(&mp_stream_write_obj)               },

    /* class constants. */
    { MP_ROM_QSTR(MP_QSTR_PARITY_NONE), MP_ROM_INT(UART_PARITY_NONE ) },
    { MP_ROM_QSTR(MP_QSTR_PARITY_EVEN), MP_ROM_INT(UART_PARITY_EVEN ) },
    { MP_ROM_QSTR(MP_QSTR_PARITY_ODD ), MP_ROM_INT(UART_PARITY_ODD  ) },
};
STATIC MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_table);


STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode)
{
    const machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);

    if (self->uart_id == 0)
    {
        mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("UART(0) can't read"));
    }

    // make sure we want at least 1 char
    if (size == 0)
    {
        return 0;
    }

    // wait for first char to become available
    //if (!uart_rx_wait(self->timeout * 1000)) {
    //    *errcode = MP_EAGAIN;
    //    return MP_STREAM_ERROR;
    //}

    // read the data
    uint8_t *buf = buf_in;
    for (;;)
    {
        *buf++ = machine_uart_rx_data(self->uart_id);
        if (--size == 0 /* || !uart_rx_wait(self->timeout_char * 1000)*/ )
        {
            // return number of bytes read
            return buf - (uint8_t *)buf_in;
        }
    }
}

STATIC mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode)
{
    machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in);
    const byte *buf = buf_in;

    /* TODO implement non-blocking
    // wait to be able to write the first character
    if (!uart_tx_wait(self, timeout)) {
        *errcode = EAGAIN;
        return MP_STREAM_ERROR;
    }
    */

    // write the data
    for (size_t i = 0; i < size; ++i)
    {
        machine_uart_tx_data(self->uart_id, *buf++);
    }

    // return number of bytes written
    return size;
}

STATIC mp_uint_t machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode)
{
    machine_uart_obj_t * self = (machine_uart_obj_t *)self_in;
    mp_uint_t ret;

    if (request == MP_STREAM_POLL)
    {
        mp_uint_t flags = arg;
        ret = 0;
        if ( (flags & MP_STREAM_POLL_RD) && machine_uart_rx_any(self->uart_id) )
        {
            ret |= MP_STREAM_POLL_RD;
        }
        //if ((flags & MP_STREAM_POLL_WR) && uart_tx_any_room(self->uart_id))
        if (flags & MP_STREAM_POLL_WR)
        {
            ret |= MP_STREAM_POLL_WR;
        }
    }
    else
    {
        *errcode = MP_EINVAL;
        ret = MP_STREAM_ERROR;
    }
    return ret;
}

STATIC const mp_stream_p_t uart_stream_p =
{
    .read  = machine_uart_read,
    .write = machine_uart_write,
    .ioctl = machine_uart_ioctl,
    .is_text = false,
};

const mp_obj_type_t machine_uart_type =
{
    { &mp_type_type },
    .name     = MP_QSTR_UART,
    .print    = machine_uart_obj_print, /* __repr__(), which would be called by print(<ClassName>). */
    .make_new = machine_uart_obj_make_new, /* create new class instance. */
    .getiter  = mp_identity_getiter,
    .iternext = mp_stream_unbuffered_iter,
    .protocol = &uart_stream_p,
    .locals_dict = (mp_obj_dict_t *)&machine_uart_locals_dict,
};

/*
 * User functions.
 */
/* 格式化uart对象，传入参数无论是已经初始化好的uart对象，还是一个表示uart清单中的索引编号，通过本函数都返回一个期望的uart对象。 */
const machine_uart_obj_t *uart_find(mp_obj_t user_obj)
{
    /* 如果传入参数本身就是一个uart的实例，则直接送出这个UART。 */
    if ( mp_obj_is_type(user_obj, &machine_uart_type) )
    {
        return user_obj;
    }

    /* 如果传入参数是一个uart通道号，则通过索引在UART清单中找到这个通道，然后送出这个通道。 */
    if ( mp_obj_is_small_int(user_obj) )
    {
        uint8_t uart_idx = MP_OBJ_SMALL_INT_VALUE(user_obj);
        if ( uart_idx < machine_uart_num )
        {
            return machine_uart_objs[uart_idx];
        }
    }

    mp_raise_ValueError(MP_ERROR_TEXT("UART doesn't exist"));
}

/* EOF. */

